#include "useHead.h"
#include "stringTable.h"
#include "instrStream.h"

#define NEWLINE_COUNT 4

Instr &InstrStream::getInstruction(int index)
{
	if (index >= _instrStream.size()) {
		printf("InstrStream index out of range!\n");
		return _instrStream[0];
	}

	return _instrStream[index];
}


const string &InstrStream::codeName(CODE opcode)
{
    static string NullRet;

	map<CODE, string>::iterator Iter = _code2Name.find(opcode);
	if (Iter == _code2Name.end())
		return NullRet;

	return Iter->second;
}

CODE  InstrStream::nameCode(const string &name)
{
   
	map<string, CODE>::iterator Iter = _name2Code.find(name);
	if (Iter == _name2Code.end())
		return 255;

	return Iter->second;
}

int InstrStream::cfgAdd(const string &name, CODE opcode, COUNT opcount) 
{
	map<CODE, string>::iterator Iter;

	Iter = _code2Name.find(opcode);
	if (Iter != _code2Name.end())
		return -1;

	_code2Name[opcode] = name;
	_name2Code[name] = opcode;
	_instrConfig[name]._opCount = opcount;

	return 0;
}

void  InstrStream::instrList(vector<string> &instrs)
{
	instrs.clear();
	map<string, CODE>::iterator Iter = _name2Code.begin();
	
	while (Iter != _name2Code.end()) {
		instrs.push_back(Iter->first);
		++Iter;
	}
}

int InstrStream::cfgSetOpType(const string &name, int opindex, OpType &op)
{
	if (opindex >= MAX_OPCOUNT || opindex < 0)
		return -1;

	map<string, AuxInstr>::iterator Iter = _instrConfig.find(name);
	if (Iter == _instrConfig.end())
		return -1;

	Iter->second._opList[opindex] = op;
	return 0;
}


int InstrStream::loadConfig(const char *file)
{
	FILE *fp;
	OpType optype;
	int  i, index, len, line = 0;
	CODE  opcode;
	COUNT opcount;
	string tempName;
	char *p, *s;
	char buf[1024];


	if (file == NULL)
		return -1;

    printf("Open config file :%s\n", file);

	if((fp = fopen(file, "r")) == NULL) {
		return -1;
	}

	map<string, OpType> instr_map;
	map<string, OpType>::iterator Iter;

	instr_map["Int"] =	    OP_FLAG_TYPE_INT;
	instr_map["Float"] = 	OP_FLAG_TYPE_FLOAT; 	
	instr_map["String"] = 	OP_FLAG_TYPE_STRING; 	
	instr_map["MemRef"] = 	OP_FLAG_TYPE_MEM_REF; 	
	instr_map["Label"] = 	OP_FLAG_TYPE_INSTR_INDEX; 	
	instr_map["FuncName"] = OP_FLAG_TYPE_FUNC_INDEX; 	
	instr_map["HostAPI"] = 	OP_FLAG_TYPE_HOST_API_CALL; 	
	instr_map["Reg"] = 	    OP_FLAG_TYPE_REG;

	while (fgets(buf, sizeof(buf), fp)) {
		++line;
		
		if (buf[0] == '#' || buf[0] == '\n')
			continue;

		p = strtok(buf, " \t");
		s = strtok(NULL, " \t");
		if (s == NULL) {
			fprintf(stderr, "strtok error 1, line %d\n", line);
			continue;
		}

		opcode = (CODE)atoi(s);
		s = strtok(NULL, " \t");
		if (s == NULL) {
			fprintf(stderr, "strtok error 2, line %d\n", line);
			continue;
		}

		opcount = (COUNT)atoi(s);
		cfgAdd(p, opcode, opcount);
        tempName = p;

		for (i = 0; i < opcount; ++i) {
			optype = OP_FLAG_TYPE_NULL;
			if (!fgets(buf, sizeof(buf), fp)) {
				fprintf(stderr, "opcount no enough, %d line\n", line);
				return -1;
			}
			
			len = strlen(buf);
			if (buf[len-1] == '\n')
				buf[len-1] = '\0';

			s = strtok(buf, " \t");
			
			while (s) {
				Iter = instr_map.find(s);

				if (Iter == instr_map.end()) {
					fprintf(stderr, "Op Type %s not find!\n", s);
				} else {
					optype |= Iter->second;
				}

				s = strtok(NULL, " \t");
			}

			cfgSetOpType(tempName, i, optype);
            
		}
	}

	fclose(fp);
    
	return 0;
}


char *InstrStream::dumpLine(int line)
{
    int i;
    static char buf[512], *p;
    if (line < 0 || line >= _instrStream.size())
        return NULL;

    snprintf(buf, sizeof(buf), "%s ", codeName(_instrStream[line]._opCode).c_str());
    p = buf + strlen(buf);
    for (i = 0; i < _instrStream[line]._opCount; ++i)  {
        snprintf(p, sizeof(buf) - (p - buf), "%s", valueDump(_instrStream[line]._valList[i]));
        p = buf + strlen(buf);
        if (i < _instrStream[line]._opCount - 1) {
            sprintf(p, ", ");
            p += 2;
        }
    }

    *p++ = '\n';
    *p = '\0';
    return buf;
}

void  InstrStream::dump()
{
    int i, count = 0;

    puts("\n+++++++++++++++++<Instruction Stream Dump>++++++++++++++++++++++++\n");
    printf("Instruction Number : %d\n\n", _instrStream.size());

    vector<Instr>::iterator Iter = _instrStream.begin();
    while (Iter != _instrStream.end()) {
        printf("%s ", codeName(Iter->_opCode).c_str());
        for (i = 0; i < Iter->_opCount; ++i)  {
            printf("%s", valueDump(Iter->_valList[i]));
            if (i < Iter->_opCount - 1)
                printf(", ");
        }
        putchar('\n');
        ++Iter;
        
        if (++count == NEWLINE_COUNT) {
            count = 0;
            putchar('\n');
        }
    }
    puts("\n+++++++++++++++++<Instruction Stream Dump>++++++++++++++++++++++++\n");
}


const char *InstrStream::valueDump(Value &val)
{
	static char buf[1024];

	switch(val._type) {
		case OP_FLAG_TYPE_NULL:
		    sprintf(buf, "<NULL>");
		    break;
		case OP_FLAG_TYPE_INT:
		    sprintf(buf, "%d", val._vint);
		    break;
		case OP_FLAG_TYPE_FLOAT:
		    sprintf(buf, "%f", val._vfloat);
		    break;
		case OP_FLAG_TYPE_STRING:
		    sprintf(buf, "\"%s\"", val._svalue.c_str());
		    break;
		case OP_FLAG_TYPE_INSTR_INDEX:
		    sprintf(buf, "<%d>", val._vindex);
		    break;
		case OP_FLAG_TYPE_ABS_STACK_INDEX:
		    sprintf(buf, "[%d]", val._vindex);
		    break;
		case OP_FLAG_TYPE_REL_STACK_INDEX:
		    sprintf(buf, "[%d:%d]", val._vindex, val._offset);
		    break;
		case OP_FLAG_TYPE_FUNC_INDEX:
		    sprintf(buf, "{%d}", val._vindex);
		    break;
		case OP_FLAG_TYPE_HOST_API_CALL:
		    sprintf(buf, "{!%d}", val._vindex);
		    break;
		case OP_FLAG_TYPE_REG:
		    sprintf(buf, "@");
		    break;
		case OP_FLAG_TYPE_KEY_FRAME:
		    sprintf(buf, "=>%d", val._vindex);
		    break;
		case OP_FLAG_TYPE_TOP_FRAME:
		    sprintf(buf, "%d/%d", val._vindex, val._offset);
		    break; 
		default:
		    sprintf(buf, "(?)");
		    break;
	}

	return buf;
}



int   InstrStream::readFile(FILE *fp, const StringTable &st)
{
#define FREAD(x, type)  nread = fread(&(x), sizeof(type), 1, fp);\
			if (nread != 1) { \
				fprintf(stderr, "Error! %s %s %d\n", __FILE__, __FUNCTION__, __LINE__); \
				return -1;\
			}
	int size;
	int nread;
	Value *pval;
	Instr instr;

	FREAD(size, int);
    
	_instrStream.resize(size);

	for (int i = 0; i < size; ++i) {
		FREAD(instr._opCode, CODE);	
		FREAD(instr._opCount, COUNT);
        
	
		pval = instr._valList;
		for (COUNT j = 0; j < instr._opCount; ++j) {
			FREAD(pval->_type, OpType);

			switch(pval->_type) {
				case OP_FLAG_TYPE_INT:
					FREAD(pval->_vint, int);
					break;
				case OP_FLAG_TYPE_FLOAT:
					FREAD(pval->_vfloat, float);
					break;
				case OP_FLAG_TYPE_STRING:
				case OP_FLAG_TYPE_INSTR_INDEX:
				case OP_FLAG_TYPE_HOST_API_CALL:
				case OP_FLAG_TYPE_FUNC_INDEX:
				case OP_FLAG_TYPE_ABS_STACK_INDEX:
				case OP_FLAG_TYPE_REL_STACK_INDEX:
					FREAD(pval->_vindex, int);

					if (pval->_type == OP_FLAG_TYPE_STRING)
						pval->_svalue = st.getIndex(pval->_vindex);
					if (pval->_type == OP_FLAG_TYPE_REL_STACK_INDEX) {
						FREAD(pval->_offset, int);
					}
					
					break;
                case OP_FLAG_TYPE_REG:
                    break;
				default:
					fprintf(stderr, "Unkonw Type!\n");
					return -1;
			}
			++pval;
		}	
	
		_instrStream[i] = instr;
    }

#undef FREAD
    return 0;
}

int InstrStream::writeFile(FILE *fp)
{
#define FWRITE(x, type)  nwrite = fwrite(&(x), sizeof(type), 1, fp); \
			 if (nwrite != 1) {\
				fprintf(stderr, "Error! %s %s %d\n", __FILE__, __FUNCTION__, __LINE__); \
				return -1;\
			}
			
	int nwrite;	 
	int size = _instrStream.size();
	Value *pval;

    
	FWRITE(size, int);
    
	for (int i = 0; i < size; ++i) {
		Instr &instr = _instrStream[i];
		FWRITE(instr._opCode, CODE);	
		FWRITE(instr._opCount, COUNT);

		pval = instr._valList;
		for (COUNT j = 0; j < instr._opCount; ++j) {
			FWRITE(pval->_type, OpType);

			switch(pval->_type) {
				case OP_FLAG_TYPE_INT:
					FWRITE(pval->_vint, int);
					break;
				case OP_FLAG_TYPE_FLOAT:
					FWRITE(pval->_vfloat, float);
					break;
				case OP_FLAG_TYPE_STRING:
				case OP_FLAG_TYPE_INSTR_INDEX:
				case OP_FLAG_TYPE_HOST_API_CALL:
				case OP_FLAG_TYPE_FUNC_INDEX:
				case OP_FLAG_TYPE_ABS_STACK_INDEX:
				case OP_FLAG_TYPE_REL_STACK_INDEX:
					FWRITE(pval->_vindex, int);

					if (pval->_type == OP_FLAG_TYPE_REL_STACK_INDEX) {
						FWRITE(pval->_offset, int);
					}
					
					break;
                case OP_FLAG_TYPE_REG:
                    break;

				default:
					fprintf(stderr, "Unkonw Type!\n");
					return -1;
			}
			++pval;
		}	
	}
#undef FWRITE
}
